/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
// -*- coding:iso-8859-15 mode: c++ -*-
#ifndef _JOYSTICKCONTROLCOMPONENT_H_
#define _JOYSTICKCONTROLCOMPONENT_H_

#include <string>

//#include "settings.hpp"
#include "JoystickControlUtils.h"
#include "JoystickControlEvent.h"
#include "wx/sizer.h"
#include "wx/panel.h"

// < changeId (=axis/button id), optionId >
typedef std::map< int, int > t_changeIdReferencesList;
typedef t_changeIdReferencesList::iterator t_changeIdReferencesListIterator;
typedef t_changeIdReferencesList::const_iterator t_changeIdReferencesListConstIterator;
typedef std::pair< int, int > t_changeIdReferencesPair;

struct ChangeIdReferences {
  t_changeIdReferencesList axes;
  t_changeIdReferencesList buttons;
};

//       < joystickId, axes/buttons >
typedef std::map< int, ChangeIdReferences > t_joystickStatusList;
typedef t_joystickStatusList::iterator t_joystickStatusListIterator;
typedef t_joystickStatusList::const_iterator t_joystickStatusListConstIterator;
typedef std::pair< int, ChangeIdReferences > t_joystickStatusListPair;

// Contains optionid and description.
typedef std::pair< int, std::string > t_selectOptionsPair;

// Structure for returning possible options.
typedef std::vector< t_selectOptionsPair > t_optionList;
typedef t_optionList::iterator t_optionListIterator;
typedef t_optionList::const_iterator t_optionListConstIterator;

struct OptionValues {
  OptionValues(const std::string optName);
  
  std::string optionName;
  float optionValue; ///< 0 or 1 for buttons, -1.00 - 1.00 for axes.
  bool optionChanged; ///< If option has changed since last read.
};

// Structure for storing options and their descriptions.
typedef std::map< int, OptionValues > t_optionContainer;
typedef t_optionContainer::iterator t_optionContainerIterator;
typedef t_optionContainer::const_iterator t_optionContainerConstIterator;
typedef std::pair< int, OptionValues > t_optionsPair;

// Structures for storing configurations.
struct ConfigOption {
  std::string optionName;
  int optionId;
};

struct Configuration {
  std::string name;
  std::vector<ConfigOption> axisConfigurations;
  std::vector<ConfigOption> buttonConfigurations;
};

typedef std::vector<Configuration> t_configList;
typedef t_configList::iterator t_configListIterator;
typedef t_configList::const_iterator t_configListConstIterator;


struct JoystickOption{
  JoystickOption(int aOptionId, int aJoystickId, int aButtonOrAxisId):
    optionId(aOptionId),
    joystickId(aJoystickId),
    buttonOrAxisId(aButtonOrAxisId){}
    int optionId;
    int joystickId;
   int buttonOrAxisId;
};

class ComponentConfiguration {
  public:
    ComponentConfiguration();
    ~ComponentConfiguration();
    
    //// "Eventhandler", Write new values to options.
    /**
     * New joystickevents are send here, handles changed axes/buttons and
     * updates the options that have changed.
     */
    void handleNewEvent(const GIMUIJoystickEvent &newEvent);
    
    //// List options.
    /**
     * List possible selection-options for axes.
     */
    int listAxisOptions(t_optionList &options) const;
    
    /**
     * List possible selection-options for buttons.
     */
    int listButtonOptions(t_optionList &options) const;
    
    //// Select axis/button for an option.
    /**
     * Sets certain axis-option to use axis x from joystick y.
     * To prevent axes from getting stuck in their old value, value is reset
     * to 0.
     *
     * \param optionId OptionId -1 is used to empty options.
     */
  int setAxisOption(const int optionId,
                      const int joystickId,
                      const int axisId);
  
  
    int clearAxisOptions();
    
    /**
     * Sets certain button-option to use button x from joystick y.
     * To prevent buttons from getting stuck in their old value, value is
     * reset to 0.
     *
     * \param optionId OptionId -1 is used to empty options.
     */
    int setButtonOption(const int optionId,
                        const int joystickId,
                        const int axisId);
 
    int clearButtonOptions();
    
    //// Add options.
    /**
     * Adds an axis-option.
     *
     * \return JOYSTICKCONTROL_OK or JOYSTICKCONTROL_ERROR if optionId was in use.
     */
    int addAxisOption(const int optionId,
                      const std::string &optionDescription);
                      
    /**
     * Adds a button-option.
     *
     * \return JOYSTICKCONTROL_OK or JOYSTICKCONTROL_ERROR if optionId was in use.
     */
    int addButtonOption(const int optionId,
                        const std::string &optionDescription);
       
    //// Read values from options.
    /**
     * Reads button value and indicates if value has changed since last read.
     * \param optionId Id-number of wanted button-option.
     * \param value If optionId is found, value is written here. False is
     * used if optionId is not found.
     * \return JOYSTICKCONTROL_STATUS_CHANGED if value had changed,
     * JOYSTICKCONTROL_STATUS_UNCHANGED if value had not changed, or
     * JOYSTICKCONTROL_ERROR if optionId was not found.
     */
    int readButtonOption(const int optionId,
                         bool *value);
  
    /**
     * Reads axis value and indicates if value has changed since last read.
     * \param optionId Id-number of wanted button-option.
     * \param value If optionId is found, value is written here. 0 is
     * used if optionId is not found.
     * \return JOYSTICKCONTROL_STATUS_CHANGED if value had changed,
     * JOYSTICKCONTROL_STATUS_UNCHANGED if value had not changed, or
     * JOYSTICKCONTROL_ERROR if optionId was not found.
     */
    int readAxisOption(const int optionId,
                       float *value);
    
  private:
    //All the containers for axisoptions and buttonoptions.
    ownMutexHandle optionsMutex;
    t_optionContainer axisOptions;
    t_optionContainer buttonOptions;
    
    ownMutexHandle joysticksMutex;
    t_joystickStatusList joysticks;
};

/**
 * \class JoystickControlComponent Interface for creating control-components that receive joystick
 * events.
 * 
 * Implement your own component by inheriting this, and implementing the virtual methods
 * Component should also add axis- and button-options in initializer.
 */
namespace gimi {
  class GIMI;
}
class JoystickControlComponent {
  public:
  JoystickControlComponent(  gimi::GIMI *aGIMIPtr, 
                             wxSizer *aComponentSizer,
                             wxPanel *aComponentPanel,
                             const std::string aComponentName,
                             const int aComponentOutputType,
                             const int aComponentInputType);
    virtual ~JoystickControlComponent() = 0;
    
    /**
     * When component has registered to receive certain events, then this
     * method is called whenever specified event is received from joystick.
     *
     * If a component wants to bypass the option-id based eventhandling,
     * it can make it's own implementation of this method.
     */
    virtual void eventReceiver(const GIMUIJoystickEvent &event);
    
    /**
     * Returns name of the component.
     */
    std::string getComponentName() const;
    
    /**
     * Returns the datatype (majortype) that tells what data the component
     * sends to target. If component does not send data, it should return -1.
     */
    int getComponentOutputType() const;
    
    /**
     * Returns the datatype (majortype) that tells what data the component can
     * receive from target. If component does not receive data, it should
     * return -1.
     */
    int getComponentInputType() const;
    
    //// Configuration, implemented in this class, not in child.
    /**
     * Returns list of axis-options for configuration.
     */
    int listAxisOptions(t_optionList &options) const;
    
    /**
     * Returns list of button-options for configuration.
     */
    int listButtonOptions(t_optionList &options) const;
    
    /**
     * Sets certain axis-option to use axis x from joystick y.
     *
     * \param optionId OptionId -1 is used to empty options.
     * \return JOYSTICKCONTROL_OK for success or JOYSTICKCONTROL_ERROR if
     * optionId was wrong.
     */
    int setAxisOption(const int optionId,
                      const int joystickId,
                      const int axisId);

    bool getSetAxisOption(const int optionId);

    /**
     * Sets certain button-option to use button x from joystick y.
     *
     * \param optionId OptionId -1 is used to empty options.
     * \return JOYSTICKCONTROL_OK for success or JOYSTICKCONTROL_ERROR if
     * optionId was wrong.
     */
    int setButtonOption(const int optionId,
                        const int joystickId,
                        const int buttonId);

  bool isAxisOptionIdSet(const int aOptionId);    
  bool isButtonOptionIdSet(const int aOptionId);    
    //// Optional methods, implement if your component uses them.
    /**
     * Adds a target for component. Optional, no need to implement if component
     * does not send data.
     */
    virtual int addTarget(const gimi::GIMIService &aTarget) { return -1; }
    virtual int removeTarget(const gimi::GIMIService &aTarget) { return -1; }
    virtual int clearTargets() { return -1; }
    
    /**
     * Starts the component.
     * \return True for success, false for failure.
     */
    virtual bool startComponent() = 0;
    
  protected:
    ComponentConfiguration configOptions; ///< Contains configuration and
                                          ///< latest values for options.
    wxSizer *componentSizer; ///< Sizer in which component can draw it's own
                             ///< graphics.
    wxPanel *componentPanel; /// Panel in which the sizer is located.
    gimi::GIMI *iGIMIPtr;
  public:
    gimi::ActiveStatus activationStatus;
  private:
    // Disable copy-constructors.
    JoystickControlComponent(JoystickControlComponent& original);
    const JoystickControlComponent& operator= (const JoystickControlComponent& right);
    
    std::string componentName;
    int componentOutputType;
    int componentInputType;
  std::vector<JoystickOption> iSetAxisJoystickOptions;
  std::vector<JoystickOption> iSetButtonJoystickOptions;
};

#endif // _JOYSTICKCONTROLCOMPONENT_H_
